#
# Cxx.mk
# Makefile for building C++ project's executables.
# This makefile depends on Common.mk being included.
#

#
# Includes.
#
include Boost.mk

#
# Variables.
#

#
# Minimum GCC Version.
#
GCC_MIN_VERSION := 4.6.0

#
# GCC Version variable.
#
# Variable that holds which version of GCC the build process is using.
#
GCC_VERSION := $(shell gcc --version | grep ^gcc | sed 's/^.* //g')

#
# Define project's main and test executable's names.
#
main_NAME := TP1MetNum
test_NAME := $(main_NAME)Tests

#
# Define project's source and object files.
#

#
# C++ source file names for project's main executable.
#
main_CXX_SRC_NAMES := CommandLineParser.cpp chrono_io.cpp MethodFactory.cpp Program.cpp TP1MetNum.cpp

#
# C++ source file names for project's test executable.
#
test_CXX_SRC_NAMES := BisectionMethodTests.cpp FixedPointMethodTests.cpp NewtonMethodTests.cpp SecantMethodTests.cpp TP1MetNumTests.cpp

#
# C++ source file names shared by both main and test project's executables.
#
shared_CXX_SRC_NAMES := BinaryIterativeMethod.cpp BisectionMethod.cpp FixedPointMethod.cpp IterativeMethod.cpp NewtonMethod.cpp SecantMethod.cpp UnaryIterativeMethod.cpp

#
# Project's C++ source file names.
#
project_CXX_SRC_NAMES := $(main_CXX_SRC_NAMES) $(test_CXX_SRC_NAMES) $(shared_CXX_SRC_NAMES)

#
# C++ source files for project's main executable.
#
main_CXX_SRCS := $(addprefix $(CXX_SRC_DIR)/, ${main_CXX_SRC_NAMES})

#
# C++ source files for project's test executable.
#
test_CXX_SRCS := $(addprefix $(CXX_SRC_DIR)/, ${test_CXX_SRC_NAMES})

#
# C++ source files shared by both main and test project's executables.
#
shared_CXX_SRCS := $(addprefix $(CXX_SRC_DIR)/, ${shared_CXX_SRC_NAMES})

#
# Object files for project's main executable.
#
main_OBJS := $(addprefix $(OBJ_DIR)/, ${main_CXX_SRC_NAMES:.cpp=.o})

#
# Object files for projet's test executable.
#
test_OBJS := $(addprefix $(OBJ_DIR)/, ${test_CXX_SRC_NAMES:.cpp=.o})

#
# Object files shared by both main and test project's executables.
#
shared_OBJS := $(addprefix $(OBJ_DIR)/, ${shared_CXX_SRC_NAMES:.cpp=.o})

#
# Main Target object files.
#
main_TARGET_OBJS := $(main_OBJS) $(shared_OBJS)

#
# Test Target object files.
#
test_TARGET_OBJS := $(test_OBJS) $(shared_OBJS)

#
# Define project's dependencies.
#

#
# Project's include directories.
#
project_INCLUDE_DIRS := $(INCLUDE_DIR)

#
# Project's library directories.
#
project_LIBRARY_DIRS := $(LIBRARY_DIR)

#
# Project's libraries.
#
project_LIBRARIES := boost_program_options boost_unit_test_framework

#
# Update preprocessor, compiler and linker flags.
#

#
# Update CPPFLAGS to link Boost dynamically.
#
CPPFLAGS += -D BOOST_ALL_DYN_LINK
 
#
# Update CPPFLAGS to take into account project's include directories.
#
CPPFLAGS += $(foreach include_dir, $(project_INCLUDE_DIRS), -I$(include_dir))

#
# Update the C++ compiler flags.
#
CXXFLAGS += -O2 -ggdb -W -Wall -Wextra -pedantic -fmessage-length=0 $(call gcc_cxx_std_option)

#
# Update LDFLAGS to take into account project's library directories.
#
LDFLAGS += $(foreach library_dir, $(project_LIBRARY_DIRS), -L$(library_dir))

#
# Update LDFLAGS to take into accout project's libraries.
#
LDFLAGS += $(foreach library, $(project_LIBRARIES), -l$(library))


#
# Functions.
#

#
# gcc_version_integer function.
#
# Returns an integer value from the GCC version string supplied.
#
# Arguments:
#	$(1)	-- gcc version string.
#
gcc_version_integer = $(shell echo $(1) | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$//')

#
# is_gcc_46 function.
#
# Returns whether the supplied GCC version string is from GCC 4.6.
#
# Arguments:
#	$(1)	-- gcc version string.
#
is_gcc_46 = $(call is_gcc_46_internal,$(call gcc_version_integer,$(1)))

#
# is_gcc_46_internal function.
#
# Regutns whether the supplied GCC version number is from GCC 4.6.
#
# Arguments:
#	$(1)	-- gcc version number.
#
is_gcc_46_internal = $(and						   \
	$(call compare_integers,$(1),ge,$(call gcc_version_integer,4.6.0)),\
	$(call compare_integers,$(1),lt,$(call gcc_version_integer,4.7.0)))

#
# ggc_cxx_std_option function.
#
# Returns the option used by the currect GCC compiler to
# select the C++11 standard as the supported language version.
#
# Arguments:
#	none
#
gcc_cxx_std_option = $(if $(call is_gcc_46,$(GCC_VERSION)),-std=c++0x,-std=c++11)


#
# Canned Recipes.
#

#
# Check GCC Version
#
define check_gcc_version
	$(if $(call compare_integers,$(call gcc_version_integer,$(GCC_VERSION)),lt,$(call gcc_version_integer,$(GCC_MIN_VERSION))),$(error GCC version $(GCC_VERSION) not supported. Please use at least $(GCC_MIN_VERSION)))
endef


#
# Targets.
#

#
#
# Define the project's executable targets.
#
main_TARGET = $(BIN_DIR)/$(main_NAME)
test_TARGET = $(BIN_DIR)/$(test_NAME)

#
# Define the list of .PHONY targets.
#
.PHONY: all check cxx-clean cxx-distclean main cxx-realclean run test

#
# all target.
# Builds both main and test executables.
#
all: main test

#
# check target.
# Builds and runs project's tests.
#
check: test
	@ export $(dl_environment_variable_name)=$(CURDIR)/$(LIBRARY_DIR); $(test_TARGET)

#
# check-gcc-version
# Verifies the GCC version in use.
#
check-gcc-version:
	$(check_gcc_version)

#
# cxx-clean target.
# Delete project's C++ generated files.
#
cxx-clean:
	@ rm -rf $(BIN_DIR)
	@ rm -rf $(OBJ_DIR)

#
# cxx-distclean target.
# Deletes project's C++ generated files and dependency directories.
#
cxx-distclean: cxx-clean
	@ rm -rf $(INCLUDE_DIR)
	@ rm -rf $(LIBRARY_DIR)
	@ rm -rf $(BOOST_DIST_DIR)

#
# main target.
# Alias for the $(main_TARGET) target.
#
main: $(main_TARGET)

#
# cxx-realclean target.
# Deletes project's C++ generated files, dependency directories and dependency distributions.
#
cxx-realclean: cxx-distclean
	@ rm -rf $(DIST_DIR)

# run target.
# Runs the main project executable.
#
run: main
	@ export $(dl_environment_variable_name)=$(CURDIR)/$(LIBRARY_DIR); $(main_TARGET)

#
# test target.
# Alias for the $(test_TARGET) target.
#
test: $(test_TARGET)

#
# run target.
# Builds and runs project's main executable.
#
#run: main
#	@ $(main_TARGET)

#
# $(MAIN_TARGET)
# Builds the project main executable.
#
$(main_TARGET) : $(main_TARGET_OBJS) | check-gcc-version $(BOOST) $(BIN_DIR)
	@ echo Linking $(main_NAME)
	@ $(CXX) -o $@ $^ $(CXXFLAGS) $(LDFLAGS)

#
# $(TEST_TARGET)
# Builds the project test executable.
#
$(test_TARGET) : $(test_TARGET_OBJS) | check-gcc-version $(BOOST) $(BIN_DIR)
	@ echo Linking $(test_NAME)
	@ $(CXX) -o $@ $^ $(CXXFLAGS) $(LDFLAGS)

#
# Rule for building the object files.
#
$(OBJ_DIR)/%.o : $(CXX_SRC_DIR)/%.cpp | check-gcc-version $(BOOST) $(OBJ_DIR)
	@ echo Compiling $<
	@ $(CXX) -c $< -o $@ $(CPPFLAGS) $(CXXFLAGS) $(TARGET_ARCH)

